local tArgs = {...}

local parentShell = shell

local bExit = false
local sDir = (parentShell and parentShell.dir()) or ""
local sPath = (parentShell and parentShell.path()) or ".:/rom/programs"
local tAliases = (parentShell and parentShell.aliases()) or {}
local tProgramStack = {}

local shell = {}
local tEnv = {
	["shell"] = shell,
}

local function findArg(arg)
 for _,v in ipairs(tArgs) do
  if v == arg then
   return true
  end
 end
 return false
end

-- Colours
local promptColour, textColour, bgColour
if term.isColour() then
	promptColour = colours.yellow
	textColour = colours.white
	bgColour = colours.black
else
	promptColour = colours.white
	textColour = colours.white
	bgColour = colours.black
end


local function run( _sCommand, ... )
	local sPath = shell.resolveProgram( _sCommand )
	if sPath ~= nil then
		tProgramStack[#tProgramStack + 1] = sPath
   		local result = os.run( tEnv, sPath, ... )
		tProgramStack[#tProgramStack] = nil
		return result
   	else
    	printError( "No such program" )
    	return false
    end
end

local function runLine( _sLine )
	local tWords = {}
	for match in string.gmatch( _sLine, "[^ \t]+" ) do
		table.insert( tWords, match )
	end

	local sCommand = tWords[1]
	if sCommand then
		return run( sCommand, unpack( tWords, 2 ) )
	end
	return false
end

-- Install shell API
function shell.run( ... )
	return runLine( table.concat( { ... }, " " ) )
end

function shell.exit()
    bExit = true
end

function shell.dir()
	return sDir
end

function shell.setDir( _sDir )
	sDir = _sDir
end

function shell.path()
	return sPath
end

function shell.setPath( _sPath )
	sPath = _sPath
end

function shell.resolve( _sPath )
	local sStartChar = string.sub( _sPath, 1, 1 )
	if sStartChar == "/" or sStartChar == "\\" then
		return fs.combine( "", _sPath )
	else
		return fs.combine( sDir, _sPath )
	end
end

function shell.resolveProgram( _sCommand )
	-- Substitute aliases firsts
	if tAliases[ _sCommand ] ~= nil then
		_sCommand = tAliases[ _sCommand ]
	end

    -- If the path is a global path, use it directly
    local sStartChar = string.sub( _sCommand, 1, 1 )
    if sStartChar == "/" or sStartChar == "\\" then
    	local sPath = fs.combine( "", _sCommand )
    	if fs.exists( sPath ) and not fs.isDir( sPath ) then
			return sPath
    	end
		return nil
    end
    
 	-- Otherwise, look on the path variable
    for sPath in string.gmatch(sPath, "[^:]+") do
    	sPath = fs.combine( shell.resolve( sPath ), _sCommand )
    	if fs.exists( sPath ) and not fs.isDir( sPath ) then
			return sPath
    	end
    end
	
	-- Not found
	return nil
end

function shell.programs( _bIncludeHidden )
	local tItems = {}
	
	-- Add programs from the path
    for sPath in string.gmatch(sPath, "[^:]+") do
    	sPath = shell.resolve( sPath )
		if fs.isDir( sPath ) then
			local tList = fs.list( sPath )
			for n,sFile in pairs( tList ) do
				if not fs.isDir( fs.combine( sPath, sFile ) ) and
				   (_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then
					tItems[ sFile ] = true
				end
			end
		end
    end	

	-- Sort and return
	local tItemList = {}
	for sItem, b in pairs( tItems ) do
		table.insert( tItemList, sItem )
	end
	table.sort( tItemList )
	return tItemList
end

function shell.getRunningProgram()
	if #tProgramStack > 0 then
		return tProgramStack[#tProgramStack]
	end
	return nil
end

function shell.setAlias( _sCommand, _sProgram )
	tAliases[ _sCommand ] = _sProgram
end

function shell.clearAlias( _sCommand )
	tAliases[ _sCommand ] = nil
end

function shell.aliases()
	-- Add aliases
	local tCopy = {}
	for sAlias, sCommand in pairs( tAliases ) do
		tCopy[sAlias] = sCommand
	end
	return tCopy
end

-- Custom shell API functions

function shell.executeScript(path)
 if not fs.exists(path) or fs.isDir(path) then
  return "file does not exist"
 else
  local t = luaex.iterateFileLines(path)
  if string.find(t[1], "@ @ !! TESSERACT SCRIPT HEADER") or t[1] == "@ @ !! TESSERACT SCRIPT HEADER" then
   for _,cmd in pairs(t) do
    shell.run(cmd)
   end
  else
   return "script header is missing or corrupt" 
  end
 end
end

function shell.getActiveUserShell()
 if fs.exists("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") then
  local ud = dofile("/etc/passwd/.shadow/".._G["_activeUser"]..".usr")
  return ud.shell or "/bin/sh"
 else
  return "/bin/sh"
 end
end

-- Run custom shells
if findArg("--force") or findArg("-f") then
 kernel.writeMessage("ignoring user shell settings")
elseif shell.getActiveUserShell() ~= "/bin/sh" then
 shell.run(shell.getActiveUserShell())
 kernel.shutdown(false)
end
	
term.setBackgroundColor( bgColour )
term.setTextColour( promptColour )
print( os.version() )
term.setTextColour( textColour )

local label = os.getComputerLabel() or os.getComputerID()

-- Read commands and execute them
local tCommandHistory = {}
while not bExit do
	term.setBackgroundColor( bgColour )
	if security.getSU() then
		if security.getActiveUserStatus() then
	     term.setTextColour( colours.lime )
	    else
	     term.setTextColour( colours.orange )
		end
	else
	 term.setTextColour( colours.cyan )
    end
	write( _activeUser )
	term.setTextColour( colours.lightGrey )
	write( "@" )
	term.setTextColour( colours.lightBlue )
	write( label )
	term.setTextColour( colours.lightGrey )
	write( "/" )
	term.setTextColour( colours.lightBlue )
	write( shell.dir() )
	if security.getSU() then
	 if security.getActiveUserStatus() then
	  term.setTextColour( colours.lime )
	 else
	  term.setTextColour( colours.orange )
	 end
	 write( " # " )
	else
	 term.setTextColour( colours.cyan )
	 write( " $ " )
    end
	term.setTextColour( textColour )

	local sLine = read( nil, tCommandHistory )
	table.insert( tCommandHistory, sLine )
	runLine( sLine )
end

-- Custom shutdown code goes here